package cn.tedu.charging.order.endpoints;

import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 这个类是容器中一个websocket对外暴露的接口端点
 * 在端点的代码中 我们关心以下内容
 * 1. 客户端请求连接路径(基于http接口建立长连接)
 * 2. 如果用户开始建立连接,成功还是失败
 * 3. 连接一旦建立 是否断开,为什么断开
 * 4. 正常连接后,客户端给我发送的是什么消息
 * 5. 连接后的各种异常通信情况如何管理
 */
@Component
@Slf4j
@ServerEndpoint("/ws/server/{userId}")//如果要建立ws连接 前端访问的http://localhost:7070/ws/server
public class WebsocketServerPoint {
    //准备一个映射注册器 map实现类我们使用ConcurrentHashMap 线程安全
    private static final Map<Integer,Session> SESSIONS=new ConcurrentHashMap<>();
    /**
     * 当客户端成功建立和这个服务端的连接的时候,这个onOpen方法会被调用
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") Integer userId){
        log.info("有客户端建立连接,会话ID:{},用户ID:{}",session.getId(),userId);
        try{
            //调用session对象给客户端反馈一个欢迎语句
            String msg="您好,我是服务端,欢迎连接";
            session.getBasicRemote().sendText(msg);
            log.info("给客户端发送欢迎语句成功");
            //将当前的userId 和session会话对象,添加到session中 形成业务userid和连接映射关系
            SESSIONS.put(userId,session);
            //每当有一个新用户连接,map里就多了一个key-value
            log.info("当前系统在线人数:{}",SESSIONS.size());
        }catch (Exception e){
            log.error("给客户端发送欢迎语句时发生异常:{}",e);
        }
    }

    /**
     * 当客户端断开连接的时候,这个onClose方法会被调用
     */
    @OnClose
    public void onClose(Session session,@PathParam("userId") Integer userId){
        log.info("有客户端断开连接,会话ID:{},用户ID:{}",session.getId(),userId);
        //如果某个用户的连接是断开的,就无法在使用session和用户通信,将其从map中移除
        SESSIONS.remove(userId);
        log.info("当前系统在线人数:{}",SESSIONS.size());
    }

    /**
     * websocket是全双工通信,客户端可以给服务端发送消息,一旦消息达到服务端
     * 这个方法就会被调用
     */
    @OnMessage
    public void onMessage(String message,Session session,@PathParam("userId") Integer userId){
        log.info("收到客户端的消息:{},会话ID:{},用户ID:{}",message,session.getId(),userId);
    }

    /**
     * 当连接出现异常的时候,这个方法会被调用
     * 比如断网,服务端会收到异常通知
     * @param error
     */
    @OnError
    public void onError(Throwable error,Session session,@PathParam("userId") Integer userId){
        log.error("连接异常:{},会话ID:{},用户ID:{}",error,session.getId(),userId);
    }

    /*
    当前这个serverPoint类里,有一个map集合,可以根据userId拿到session连接
    从而给客户端推送消息
     */
    public boolean pushMsg(Integer userId,String message){
        //1.从SESSIONS拿到session对象 不一定能拿到
        Session session = SESSIONS.get(userId);
        //判断是否可以获取session
        if (session!=null){
            //说明连接是存在 可以发
            try{
                //2.发送消息
                session.getBasicRemote().sendText(message);
                return true;
            }catch (Exception e){
                log.error("给用户ID:{}发送消息时发生异常:{}",userId,e);
                return false;
            }
        }else{
            log.info("用户ID:{}的连接不存在",userId);
            return false;
        }
    }
}
